interface EnumDataType<ValueType> {
  [k: string]: {
    value: ValueType
    label: string
  }
}

// String
export class EnumString<T extends Readonly<EnumDataType<string>>> {
  data!: T
  constructor(x: T) {
    this.data = x

    const byValue: any = {}
    const byLabel: any = {}

    Object.keys(x).forEach((key) => {
      if (x[key]) {
        byValue[key] = x[key].value
        byLabel[key] = x[key].label
      }
    })

    this.value = byValue
    this.label = byLabel
    this.options = Object.entries(x).map((entry) => entry[1])
  }

  value: Readonly<{
    [k in keyof T]: T[k]['value']
  }>

  label: Readonly<{
    [k in keyof T]: T[k]['label']
  }>

  options: { label: string; value: string }[]

  labelFor(e: string) {
    const findLabel = Object.entries(this.data).find((entry) => {
      return entry[1].value === e
    })

    const value = Array.isArray(findLabel) && findLabel.length >= 1 && findLabel[1]
    return value ? value.label : ''
  }

  valueFor(e: string) {
    const findLabel = Object.entries(this.data).find((entry) => {
      return entry[1].label === e
    })

    const value = Array.isArray(findLabel) && findLabel.length >= 1 && findLabel[1]
    return value ? value.value : ''
  }

  static init<T extends Readonly<EnumDataType<string>>>(e: T) {
    return new EnumString(e)
  }
}

// Number
export class EnumNumber<T extends Readonly<EnumDataType<number>>> {
  data!: T
  constructor(x: T) {
    this.data = x

    const byValue: any = {}
    const byLabel: any = {}

    Object.keys(x).forEach((key) => {
      if (x[key]) {
        byValue[key] = x[key].value
        byLabel[key] = x[key].label
      }
    })

    this.value = byValue
    this.label = byLabel
    this.options = Object.entries(x).map((entry) => entry[1])
  }

  value: Readonly<{
    [k in keyof T]: T[k]['value']
  }>

  label: Readonly<{
    [k in keyof T]: T[k]['label']
  }>

  options: { label: string; value: number }[]

  labelFor(e: number) {
    const findLabel = Object.entries(this.data).find((entry) => {
      return entry[1].value === e
    })

    const value = Array.isArray(findLabel) && findLabel.length >= 1 && findLabel[1]
    return value ? value.label : ''
  }

  valueFor(e: string) {
    const selectedItem = Object.entries(this.data).find((entry) => {
      return entry[1].label === e
    })

    const item = Array.isArray(selectedItem) && selectedItem.length >= 1 && selectedItem[1]
    return item ? item.value : ''
  }

  static init<T extends Readonly<EnumDataType<number>>>(e: T) {
    return new EnumNumber(e)
  }
}
